package edu.mercer.sse.ldap;

import edu.mercer.sse.app.Config;

import java.security.GeneralSecurityException;
import javax.net.ssl.SSLSocketFactory;

import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPConnectionOptions;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.util.ssl.SSLUtil;
import com.unboundid.util.ssl.TrustAllTrustManager;

import com.google.common.base.Optional;
import static com.google.common.base.Preconditions.checkArgument;

/**
 * 
 * LDAP Connection.<p>
 * 
 * Singleton LDAP connection for resource conservation. Lazy-initialization.
 * <p>
 * 
 * Relies on parent application to call {@link #disconnect()}.<p>
 *
 */

public class LDAPConnector
{
	private static LDAPCredentials _ldapCredentials = new LDAPCredentials();

	private static Optional<LDAPConnection> _ldapCon = Optional.absent();		
	

	/**
	 * 
	 * LDAP Connection.<p>
	 * 
	 * Singleton LDAP connection for resource conservation. 
	 * Lazy-initialization.<p>
	 * 
	 * Relies on parent application to call {@link #disconnect()}.<p>
	 *
	 */
	
	public LDAPConnector( final LDAPCredentials credentials )
	{
		super();
		
		withCredentials( credentials );
		
	}//constructor

	
	public LDAPConnector()
	{
		super();
		
	}//constructor
	
	
	public void connect() throws LDAPException, 
								 GeneralSecurityException
	{
		disconnect();
		
		init();
		
	}//method
	
	
	/**
	 * Close LDAP connection.<p> 
	 */
	
	public static void disconnect()
	{
		if( _ldapCon.isPresent() ) _ldapCon.get().close();
		
	}//method
	
	
	public static LDAPConnection con() throws LDAPException, 
									   		  GeneralSecurityException
	{ 
		return _ldapCon.or( init() );
		
	}//method
	

	private static LDAPConnection init() throws LDAPException, 
										 		GeneralSecurityException
	{
		/* allow override of ldap login / fall back to application defaults */
		
		if( ! _ldapCredentials.isSet() )
		{
			_ldapCredentials = _ldapCredentials
					
					.withAdmin( 	Config.settings()
										  .getProperty( "ldap-manager-dn" ) )
										  
					.withPassword(  Config.settings()
										  .getProperty( "ldap-password" ) )
										  
					.withHost( 		Config.settings()
										  .getProperty( "ldap-host" ) )
										  
					.withPort( 		Integer.valueOf( Config.settings()
										   .getProperty( "ldap-port" ) ) );			
		}//if
		
		final LDAPConnectionOptions ldapConOpt = new 
				LDAPConnectionOptions();

		ldapConOpt.setBindWithDNRequiresPassword( true );
		
		ldapConOpt.setConnectTimeoutMillis( Integer.valueOf( 
				Config.settings()
					  .getProperty("ldap-connect-timeout")) );
		
		ldapConOpt.setCaptureConnectStackTrace( true );		

		final SSLUtil sslUtl = new SSLUtil( new TrustAllTrustManager() );
		final SSLSocketFactory sslSkFactory = sslUtl
				.createSSLSocketFactory();

		_ldapCon = Optional.of( new LDAPConnection( sslSkFactory, 
				ldapConOpt) );

		_ldapCon.get().connect( _ldapCredentials.host(), 
						 		_ldapCredentials.port() );

		_ldapCon.get().bind( _ldapCredentials.admin(),
					  		 _ldapCredentials.password() );
		
		return _ldapCon.get();
		
	}//method
	
	
	public LDAPConnector withCredentials( final LDAPCredentials credentials )
	{
		checkArgument( credentials.isSet(), "ldap credentials incomplete" );
		
		_ldapCredentials = credentials;
		
		return this;
		
	}//method
	
	
	public LDAPCredentials getCredentials()
	{
		return _ldapCredentials;
		
	}//method
	
	
}//class
